libobs_wrapper\data\output/
mod.rs1use libobs::obs_output;
18use std::collections::HashMap;
19use std::ptr;
20use std::sync::{Arc, Mutex, RwLock};
21
22use crate::data::object::{inner_fn_update_settings, ObsObjectTrait, ObsObjectTraitPrivate};
23use crate::data::ImmutableObsData;
24use crate::data::ObsDataPointers;
25use crate::runtime::ObsRuntime;
26use crate::unsafe_send::{Sendable, SmartPointerSendable};
27use crate::utils::{ObsDropGuard, OutputInfo};
28use crate::{impl_obs_drop, impl_signal_manager, run_with_obs};
29
30use crate::{
31 encoders::{audio::ObsAudioEncoder, video::ObsVideoEncoder},
32 services::ObsServiceRef,
33 utils::{ObsError, ObsString},
34};
35
36use super::ObsData;
37
38pub(crate) mod macros;
39mod traits;
40pub use traits::*;
41
42mod pipeline;
43pub use pipeline::*;
44
45mod replay_buffer;
46pub use replay_buffer::*;
47
48#[derive(Debug)]
49struct _ObsOutputDropGuard {
50 output: Sendable<*mut obs_output>,
51 runtime: ObsRuntime,
52}
53
54impl ObsDropGuard for _ObsOutputDropGuard {}
55
56impl_obs_drop!(_ObsOutputDropGuard, (output), move || unsafe {
57 libobs::obs_output_release(output.0);
59});
60
61#[derive(Debug, Clone)]
62pub struct ObsOutputRef {
77 signal_manager: Arc<ObsOutputSignals>,
79
80 settings: Arc<RwLock<ImmutableObsData>>,
82
83 hotkey_data: Arc<RwLock<ImmutableObsData>>,
85
86 curr_video_encoder: Arc<RwLock<Option<Arc<ObsVideoEncoder>>>>,
88
89 audio_encoders: Arc<RwLock<HashMap<usize, Arc<ObsAudioEncoder>>>>,
91
92 service: Arc<RwLock<Option<Arc<ObsServiceRef>>>>,
94
95 configuration_lock: Arc<Mutex<()>>,
97
98 id: ObsString,
100
101 name: ObsString,
103
104 runtime: ObsRuntime,
105
106 output: SmartPointerSendable<*mut obs_output>,
108}
109
110impl ObsOutputTraitSealed for ObsOutputRef {
111 fn new(output: OutputInfo, runtime: ObsRuntime) -> Result<Self, ObsError> {
112 let OutputInfo {
113 id,
114 name,
115 settings,
116 hotkey_data,
117 } = output;
118
119 let settings_ptr = settings.as_ref().map(|x| x.as_ptr());
120 let hotkey_data_ptr = hotkey_data.as_ref().map(|x| x.as_ptr());
121
122 let output = run_with_obs!(
123 runtime,
124 (id, name, settings_ptr, hotkey_data_ptr),
125 move || {
126 let settings_raw_ptr = match settings_ptr {
127 Some(s) => s.get_ptr(),
128 None => ptr::null_mut(),
129 };
130
131 let hotkey_data_raw_ptr = match hotkey_data_ptr {
132 Some(h) => h.get_ptr(),
133 None => ptr::null_mut(),
134 };
135
136 let id_ptr = id.as_ptr().0;
137 let name_ptr = name.as_ptr().0;
138
139 let output = unsafe {
140 libobs::obs_output_create(
142 id_ptr,
143 name_ptr,
144 settings_raw_ptr,
145 hotkey_data_raw_ptr,
146 )
147 };
148
149 if output.is_null() {
150 return Err(ObsError::NullPointer(None));
151 }
152
153 Ok(Sendable(output))
154 }
155 )??;
156
157 let output = SmartPointerSendable::new(
158 output.0,
159 Arc::new(_ObsOutputDropGuard {
160 output: output.clone(),
161 runtime: runtime.clone(),
162 }),
163 runtime.native_registry(),
164 );
165
166 let new_settings_ptr = run_with_obs!(runtime, (output), move || {
168 let new_settings_ptr = unsafe {
169 libobs::obs_output_get_settings(output.get_ptr())
171 };
172
173 if new_settings_ptr.is_null() {
174 return Err(ObsError::NullPointer(None));
175 }
176
177 Ok(Sendable(new_settings_ptr))
178 })??;
179
180 let settings = ImmutableObsData::from_raw_pointer(new_settings_ptr, runtime.clone());
181
182 let hotkey_data = match hotkey_data {
185 Some(h) => h,
186 None => ImmutableObsData::new(&runtime)?,
187 };
188
189 let signal_manager = ObsOutputSignals::new(&output, runtime.clone())?;
190 Ok(Self {
191 settings: Arc::new(RwLock::new(settings)),
192 hotkey_data: Arc::new(RwLock::new(hotkey_data)),
193
194 curr_video_encoder: Arc::new(RwLock::new(None)),
195 audio_encoders: Arc::new(RwLock::new(HashMap::new())),
196 service: Arc::new(RwLock::new(None)),
197 configuration_lock: Arc::new(Mutex::new(())),
198
199 output: output.clone(),
200 id,
201 name,
202
203 runtime,
204 signal_manager: Arc::new(signal_manager),
205 })
206 }
207
208 fn video_encoder_slot(&self) -> &Arc<RwLock<Option<Arc<ObsVideoEncoder>>>> {
209 &self.curr_video_encoder
210 }
211
212 fn audio_encoder_slots(&self) -> &Arc<RwLock<HashMap<usize, Arc<ObsAudioEncoder>>>> {
213 &self.audio_encoders
214 }
215
216 fn service_slot(&self) -> &Arc<RwLock<Option<Arc<ObsServiceRef>>>> {
217 &self.service
218 }
219
220 fn configuration_lock(&self) -> &Arc<Mutex<()>> {
221 &self.configuration_lock
222 }
223}
224
225impl ObsObjectTraitPrivate for ObsOutputRef {
226 fn __internal_replace_settings(&self, settings: ImmutableObsData) -> Result<(), ObsError> {
227 self.settings
228 .write()
229 .map_err(|_| ObsError::LockError("Failed to acquire write lock on settings".into()))
230 .map(|mut settings_lock| {
231 *settings_lock = settings;
232 })
233 }
234
235 fn __internal_replace_hotkey_data(
236 &self,
237 hotkey_data: ImmutableObsData,
238 ) -> Result<(), ObsError> {
239 self.hotkey_data
240 .write()
241 .map_err(|_| ObsError::LockError("Failed to acquire write lock on hotkey data".into()))
242 .map(|mut hotkey_lock| {
243 *hotkey_lock = hotkey_data;
244 })
245 }
246}
247
248impl ObsObjectTrait for ObsOutputRef {
249 type Native = *mut libobs::obs_output;
250
251 fn __native_handle(&self) -> SmartPointerSendable<Self::Native> {
252 self.output.clone()
253 }
254
255 fn name(&self) -> ObsString {
256 self.name.clone()
257 }
258
259 fn id(&self) -> ObsString {
260 self.id.clone()
261 }
262
263 fn runtime(&self) -> &ObsRuntime {
264 &self.runtime
265 }
266
267 fn settings(&self) -> Result<ImmutableObsData, ObsError> {
268 let r = self
269 .settings
270 .read()
271 .map_err(|_| ObsError::LockError("Failed to acquire read lock on settings".into()))?;
272
273 Ok(r.clone())
274 }
275
276 fn hotkey_data(&self) -> Result<ImmutableObsData, ObsError> {
277 let r = self.hotkey_data.read().map_err(|_| {
278 ObsError::LockError("Failed to acquire read lock on hotkey data".into())
279 })?;
280
281 Ok(r.clone())
282 }
283
284 fn update_settings(&self, settings: ObsData) -> Result<(), ObsError> {
285 let _configuration = self
286 .configuration_lock
287 .lock()
288 .map_err(|e| ObsError::LockError(e.to_string()))?;
289 if self.is_active()? {
290 return Err(ObsError::OutputAlreadyActive);
291 }
292
293 inner_fn_update_settings!(self, libobs::obs_output_update, settings)
294 }
295}
296
297impl ObsOutputTrait for ObsOutputRef {
298 fn signals(&self) -> &Arc<ObsOutputSignals> {
299 &self.signal_manager
300 }
301}
302
303impl_signal_manager!(|ptr: SmartPointerSendable<*mut libobs::obs_output>| unsafe {
304 libobs::obs_output_get_signal_handler(ptr.get_ptr())
306}, ObsOutputSignals for *mut libobs::obs_output, [
307 "start": {},
308 "stop": {code: crate::enums::ObsOutputStopSignal},
309 "pause": {},
310 "unpause": {},
311 "starting": {},
312 "stopping": {},
313 "activate": {},
314 "deactivate": {},
315 "reconnect": {},
316 "reconnect_success": {}
317]);